package com.google.webrtc.apmdemo.file;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ByteUtils {

    public static short getShort(byte[] buf) {
        return getShort(buf, testCPU());// 第二位true/false得看cpu
    }

    public static boolean testCPU() {
        if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
            return true;
        } else {
            return false;
        }
    }

    private static short getShort(byte[] buf, boolean bBigEnding) {
        if (buf == null) {
            throw new IllegalArgumentException("byte array is null!");
        }
        if (buf.length > 2) {
            throw new IllegalArgumentException("byte array size > 2 !");
        }
        short r = 0;
        if (bBigEnding) {
            for (int i = 0; i < buf.length; i++) {
                r <<= 8;
                r |= (buf[i] & 0x00ff);
            }
        } else {
            for (int i = buf.length - 1; i >= 0; i--) {
                r <<= 8;
                r |= (buf[i] & 0x00ff);
            }
        }

        return r;
    }

    /**
     * byte[] to short[]
     *
     * @param bytes bytes
     * @return short[]
     */
    public static short[] bytes2Shorts(byte[] bytes) {
        short[] s = new short[bytes.length / 2];
        for (int iLoop = 0; iLoop < s.length; iLoop++) {
            byte[] temp = new byte[2];
            for (int jLoop = 0; jLoop < 2; jLoop++) {
                temp[jLoop] = bytes[iLoop * 2 + jLoop];
            }
            s[iLoop] = getShort(temp);
        }
        return s;
    }

    /**
     * byte[] to short[] with dynamic fill
     *
     * @param bytes byte
     * @param step  步长
     * @return short []
     */
    public static short[] bytes2ShortsWithFill(byte[] bytes, int step) {
        return dynamicFill(bytes2Shorts(bytes), step);
    }

    /**
     * 动态补齐
     *
     * @param array 原始数组
     * @param step  步长
     * @return 补齐后数组
     */
    public static short[] dynamicFill(short[] array, int step) {
        int length = array.length;
        int remainder = length % step;
        if (remainder == 0) {//可以被整除
            return array;
        } else {
            short[] newArray = new short[length + (step - remainder)];
            System.arraycopy(array, 0, newArray, 0, length);//拷贝原始数据
            return newArray;
        }
    }

    /**
     * short array  to byte array
     *
     * @param data short array
     * @return byte array
     */
    public static byte[] shorts2Bytes(short[] data) {
        byte bLength = 2;
        byte[] buf = new byte[data.length * bLength];
        for (int iLoop = 0; iLoop < data.length; iLoop++) {
            byte[] temp = getBytes(data[iLoop]);
            for (int jLoop = 0; jLoop < bLength; jLoop++) {
                buf[iLoop * bLength + jLoop] = temp[jLoop];
            }
        }
        return buf;
    }

    private static byte[] getBytes(short data) {
        return getBytes(data, testCPU());
    }

    private static byte[] getBytes(int data, boolean bBigEnding) {
        byte[] buf = new byte[4];
        if (bBigEnding) {
            for (int i = buf.length - 1; i >= 0; i--) {
                buf[i] = (byte) (data & 0x000000ff);
                data >>= 8;
            }
        } else {
            for (int i = 0; i < buf.length; i++) {
                buf[i] = (byte) (data & 0x000000ff);
                data >>= 8;
            }
        }
        return buf;
    }


    public static short[] toShortArray(byte[] src) {
        int count = src.length / 2;
        short[] dest = new short[count];
        for (int i = 0; i < count; i++) {
            dest[i] = (short) ((src[i * 2] & 0xff) | ((src[2 * i + 1] & 0xff) << 8));
        }
        return dest;
    }

    /**
     * 计算音频人声能量,取值范围：0-100
     *
     * @param bytes 人声数据
     * @return double
     */
    public static double calculateRmsV2(byte[] bytes) {
        short[] buffer = toShortArray(bytes);
        double sum = 0.0;
        // 将 buffer 内容取出，进行平方和运算
        for (int i = 0; i < buffer.length; i++) {
            double sample = buffer[i] / 32768.0;
            sum += sample * sample;
        }
        double rms = Math.sqrt(sum / buffer.length);
        double volume;
        if (rms - 0.0 <= 0.001) {
            volume = 0.0;
        } else {
            volume = 20 * Math.log10(rms) + 100;
        }
        return volume;
    }

    /**
     * 这个算法比上面的算法准
     * @param audioBytes
     * @return
     */
    public static double calculateRms(byte[] audioBytes) {
        short[] audioData = new short[audioBytes.length / 2];
        ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(audioData);
        double sum = 0;
        for (short sample : audioData) {
            sum += Math.pow(sample, 2);
        }
        double rms = Math.sqrt(sum / audioData.length);
        double dbValue = 20 * Math.log10(rms);
        return dbValue;
    }


    public static byte[] mergerBytes(byte[] totalBytes, byte[] bytes) {
        if (totalBytes != null) {
            int len = totalBytes.length;
            byte[] allByte = new byte[len + bytes.length];
            System.arraycopy(totalBytes, 0, allByte, 0, len);
            System.arraycopy(bytes, 0, allByte, len, bytes.length);
            totalBytes = new byte[allByte.length];
            System.arraycopy(allByte, 0, totalBytes, 0, allByte.length);
        } else {
            totalBytes = new byte[bytes.length];
            System.arraycopy(bytes, 0, totalBytes, 0, bytes.length);
        }
        return totalBytes;
    }

    /**
     * InputStream 转 byte[]
     *
     * @param inputStream 输入流
     * @return byte[]
     */
    public static byte[] streamToBytes(InputStream inputStream) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        try {
            while ((len = inputStream.read(buffer)) != -1) {
                output.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return output.toByteArray();
    }
}
